// Copyright(c) 2020-2025 Hongjing
// All rights reserved.
//
// Author: Yang Zhu
// Update: 2020-04-07

#ifndef LATTICE_NET_DECODE_H_
#define LATTICE_NET_DECODE_H_

#include <math.h>

#include <Eigen/Dense>
#include <array>
#include <boost/json.hpp>
#include <memory>
#include <mutex>
#include <unordered_map>

#include "common/infer_common.h"
#include "common/perception_defs.h"

class LatticeNetDecode {
 public:
  struct ObjDecodeParameter {
    int32_t bbox_info_number;
    int32_t down_size;
    std::vector<float> net_range;
    std::vector<float> voxel_size;
    float nms_iou_threshold;
    std::vector<float> class_threshold;
  };

 public:
  using decode_type = std::int16_t;
  /// @brief max decode result size
  static constexpr std::uint16_t kMaxResultSize{5120U};

  /// @brief
  static constexpr std::uint32_t kMaxObjInfoSize{16};

  /// @brief max lanemarker result size
  static constexpr std::uint32_t kMaxLaneMarkerResultSize{51200U};

  /// @brief
  static constexpr std::uint32_t kMaxMapFeatureSize{6};

  explicit LatticeNetDecode() noexcept;
  ~LatticeNetDecode();
  LatticeNetDecode(LatticeNetDecode const&) = delete;
  LatticeNetDecode& operator=(LatticeNetDecode const&) = delete;
  LatticeNetDecode(LatticeNetDecode&&) = delete;
  LatticeNetDecode& operator=(LatticeNetDecode&&) = delete;

  /// @brief decode object
  /// @param dataRaw infer output raw data
  /// @param parameters decode parameters
  /// @param boundingBoxes bounding box
  /// @param err error message
  /// @return if no error return true, otherwise false
  bool DecodeObject(
      autodrive::perception::IONode* dataRaw,
      ObjDecodeParameter const& parameters,
      autodrive::perception::fixed_array<BoundingBox, kMaxBoundingBoxNum>&
          boundingBoxes,
      std::string& err) noexcept;

  /// @brief get parameter
  /// @return ObjDecodeParameter
  inline ObjDecodeParameter& GetObjDecodeParameter() {
    return object_decode_para_;
  }

  /// @brief set parameter
  /// @return ObjDecodeParameter
  inline void SetObjDecodeParameter(ObjDecodeParameter const& para) {
    object_decode_para_ = para;
  }

 private:
  /// @brief
  /// @param box_raw
  /// @param classBase
  /// @param boundingBoxes
  /// @param err
  /// @return
  // bool decode_box(InferBoxRaw<float> const& box_raw, int const& class_number,
  //                 int const& class_base, ObjDecodeParameter const&
  //                 parameters, FixedArray<BoundingBox, kMaxBoundingBoxNum>&
  //                 bounding_boxes, std::string& err) noexcept;
  bool decode_box(
      autodrive::perception::IONode* dataRaw, int const& class_number,
      int const& class_base, ObjDecodeParameter const& parameters,
      autodrive::perception::fixed_array<BoundingBox, kMaxBoundingBoxNum>&
          bounding_boxes,
      std::string& err) noexcept;

  /// @brief gen polygon
  /// @param bbox bounding box
  /// @param polygon polygon
  void gen_polygon(
      BoundingBox const& bbox,
      autodrive::perception::fixed_array<autodrive::perception::Point2f, 4>&
          polygon) noexcept;

 private:
  /// @brief object scores
  std::shared_ptr<std::array<float, kMaxResultSize>> obj_scores_ptr;
  /// @brief object scores
  std::shared_ptr<std::array<float, kMaxResultSize * 10>> obj_total_scores_ptr;
  /// @brief object labels
  std::shared_ptr<std::array<int32_t, kMaxResultSize>> obj_labels_ptr;
  /// @brief object boxes
  std::shared_ptr<std::array<float, kMaxResultSize * kMaxObjInfoSize>>
      obj_boxes_ptr;
  /// @brief nms keep
  std::shared_ptr<std::array<bool, kMaxResultSize>> obj_nms_keep_ptr;
  ///@brief map points
  std::shared_ptr<std::array<float, kMaxResultSize * kMaxMapFeatureSize>>
      map_pts_ptr_;
  /// @brief  object decode parameter
  ObjDecodeParameter object_decode_para_;
};

namespace boost::json {
inline void tag_invoke(boost::json::value_from_tag, boost::json::value& jv,
                       LatticeNetDecode::ObjDecodeParameter const& b) {
  jv = {{"bbox_info_number", b.bbox_info_number},
        {"down_size", b.down_size},
        {"net_range", value_from(b.net_range)},
        {"voxel_size", value_from(b.voxel_size)},
        {"nms_iou_threshold", b.nms_iou_threshold},
        {"class_threshold", value_from(b.class_threshold)}};
}

inline LatticeNetDecode::ObjDecodeParameter tag_invoke(
    boost::json::value_to_tag<LatticeNetDecode::ObjDecodeParameter>,
    boost::json::value const& jv) {
  auto const& obj = jv.as_object();
  return LatticeNetDecode::ObjDecodeParameter{
      boost::json::value_to<int32_t>(obj.at("bbox_info_number")),
      boost::json::value_to<int32_t>(obj.at("down_size")),
      boost::json::value_to<std::vector<float>>(obj.at("net_range")),
      boost::json::value_to<std::vector<float>>(obj.at("voxel_size")),
      boost::json::value_to<float>(obj.at("nms_iou_threshold")),
      boost::json::value_to<std::vector<float>>(obj.at("class_threshold"))};
}
}  // namespace boost::json

#endif  // LATTICE_NET_DECODE_H_
